use std::ffi::c_void;
use std::rc::Rc;

use crate::glutils::ShaderProgram;

use zmath::{Rect, Vec2};
use std::ptr;

pub enum TextureFormat {
    RGB,
    RGBA,
}

pub struct Texture {
    id: u32,
    extent: Vec2
}

impl Texture {
    pub fn new(width: u32, height: u32, format: TextureFormat) -> Rc<Self> {
        let id = unsafe {
            let mut tex = 0;
            gl::GenTextures(1, &mut tex);
            //gl::ActiveTexture(gl::TEXTURE0);
            gl::BindTexture(gl::TEXTURE_2D, tex);

            let gl_format = match format {
                TextureFormat::RGB => gl::RGB,
                TextureFormat::RGBA => gl::RGBA,
            };

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
            gl::TexImage2D(gl::TEXTURE_2D, 0, gl_format as i32, width as i32, height as i32, 0, gl_format, gl::UNSIGNED_BYTE, ptr::null());

            gl::BindTexture(gl::TEXTURE_2D, 0);

            tex
        };

        Rc::new(Self {
            id,
            extent: Vec2(width as f32, height as f32)
        })
    }

    pub fn with_pixels(width: u32, height: u32, format: TextureFormat, pixels: &[u8]) -> Rc<Self> {
        let id = unsafe {
            let mut tex = 0;
            gl::GenTextures(1, &mut tex);
            gl::BindTexture(gl::TEXTURE_2D, tex);
            
            let gl_format = match format {
                TextureFormat::RGB => gl::RGB,
                TextureFormat::RGBA => gl::RGBA,
            };

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);

            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
            gl::TexImage2D(gl::TEXTURE_2D, 0, gl_format as i32, width as i32, height as i32, 0, gl_format, gl::UNSIGNED_BYTE, pixels.as_ptr() as *const c_void);

            gl::BindTexture(gl::TEXTURE_2D, 0);

            tex
        };

        Rc::new(Self {
            id,
            extent: Vec2(width as f32, height as f32)
        })
    }

    // ------------------->
    // |          ^       U
    // |          | top
    // |
    // | <- left
    // |
    // |
    // v V
    pub fn create_region(self: &Rc<Self>, left: f32, top: f32, right: f32, bottom: f32) -> Rect {
        let u1 = left as f64 / self.extent.0 as f64;
        let v1 = top as f64 / self.extent.1 as f64;
        let u2 = right as f64 / self.extent.0 as f64;
        let v2 = bottom as f64 / self.extent.1 as f64;
        Rect::new(u1 as f32, u2 as f32, v1 as f32, v2 as f32)
    }

    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn bind(&self, position: u32) {
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0 + position);
            gl::BindTexture(gl::TEXTURE_2D, self.id);
        }
    }

    pub fn unbind(&self) {
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
    }
}